#ifndef __AGANIMATION_H__
#define __AGANIMATION_H__

#pragma once

#include "IAnimationStateNode.h"

class CAnimationRandomizer;

class CAGAnimation : public IAnimationStateNodeFactory, public IAnimationStateNode
{
public:

	enum EFlags
	{
		ensureInStack							= (1<<0),
		forceLeaveWhenFinished		= (1<<1),
		stopCurrentAnimation			= (1<<2),
		interruptCurrentAnim			= (1<<3),
		fullRootPriority					= (1<<4),
		hasVariationInputsInName	= (1<<5),
		isLooping									= (1<<6),
	};

	inline CAGAnimation( int layer )
		: m_fAnimTimeWhenPaused(0.0f)
		, m_lastUserToken(0xffffffff)
		, m_animRandomizer(NULL)
		, m_animationLength(0.0f)
		, m_layer(layer)
		, m_flags(0)
	{
	}
	
	virtual ~CAGAnimation();

	// IAnimationStateNode
	virtual void EnterState( SAnimationStateData& data, bool dueToRollback );
	virtual EHasEnteredState HasEnteredState( SAnimationStateData& data );

	virtual bool CanLeaveState( SAnimationStateData& data );
	virtual void LeaveState( SAnimationStateData& data );
	virtual void EnteredState( SAnimationStateData& data ) {}
	virtual void LeftState( SAnimationStateData& data, bool wasEntered ) {}
	virtual void Paused(SAnimationStateData& data, bool paused, float fOverrideTransTime = -1.0f);
	virtual void Update( SAnimationStateData& data );
	virtual void GetCompletionTimes( SAnimationStateData& data, CTimeValue start, CTimeValue& hard, CTimeValue& sticky );
	virtual const Params * GetParameters();
	virtual IAnimationStateNodeFactory * GetFactory() { return this; }
	virtual void DebugDraw( SAnimationStateData& data, IRenderer * pRenderer, int x, int& y, int yIncrement );
	// ~IAnimationStateNode

	// IAnimationStateNodeFactory
	virtual bool Init( const XmlNodeRef& node, IAnimationGraphPtr );
	virtual void Release();
	virtual IAnimationStateNode * Create();
	virtual const char * GetCategory();
	virtual const char * GetName();
	virtual void SerializeAsFile(bool reading, AG_FILE *file);

	virtual bool IsLessThan( IAnimationStateNodeFactory * pFactory )
	{
		AG_LT_BEGIN_FACTORY(CAGAnimation);
			AG_LT_ELEM(m_layer);
			AG_LT_ELEM(m_flags&forceLeaveWhenFinished);
			AG_LT_ELEM(m_flags&stopCurrentAnimation);
			AG_LT_ELEM(m_flags&interruptCurrentAnim);
			AG_LT_ELEM(m_flags&fullRootPriority);
			AG_LT_ELEM(m_flags&hasVariationInputsInName);
			AG_LT_ELEM(m_RandomizeSpeedMulPercent);
			AG_LT_ELEM(m_stickyOutTime);
			AG_LT_ELEM(m_animName);
			AG_LT_ELEM(m_SPSpeedMultiplier);
			AG_LT_ELEM(m_MPSpeedMultiplier);
			AG_LT_ELEM(m_stayInStateUntil);
			AG_LT_ELEM(m_forceInStateUntil);
			AG_LT_ELEM(m_CRC32AnimName);
		AG_LT_END();
	}
	// ~IAnimationStateNodeFactory

	//! Returns true if the state has an animation (with or without variation inputs) or a randomizer attached to it.
	//! It does not check whether the animation name is valid or exists.
	bool HasAnimation();

	//! will retrieve the animation id for crc and variation inputs - NOT for randomizer
	int RetrieveAnimationId( const CAnimationGraphState* pState, IAnimationSet* pAnimSet );

	Vec2 GetMinMaxSpeed( IEntity * pEntity );

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->AddObject(this, sizeof(*this));
		s->AddObject(m_animName);
	}
	
protected:
	int GetLayer() { return m_layer; }
	virtual CryCharAnimationParams GetAnimationParams( SAnimationStateData& data );

private:
	bool IsTopOfStack( const SAnimationStateData& data );
	bool IsActivated( const SAnimationStateData& data, bool * pFound = NULL );
	bool StartNodeAnimation(int id, SAnimationStateData& data, bool dueToRollback);
	bool IsAnimTopOfStack(int id, const SAnimationStateData& data) const;

	float m_stickyOutTime;
	float m_RandomizeSpeedMulPercent; // variation of speed multiplier - for randomization purposes
	float m_SPSpeedMultiplier; // single-player speed multiplier
	float m_MPSpeedMultiplier; // multi-player speed multiplier
	float m_stayInStateUntil;
	float m_forceInStateUntil;

	CCryName	m_animName;
	uint	m_CRC32AnimName;

	float m_fAnimTimeWhenPaused;

	// ***FIXME***: The following 3 members are all per-entity state, and yet are shared by all users of the same AG!
	// Until this is fixed expect unpredictable behavior!
	uint32 m_lastUserToken;
	bool m_bUseLastToken;

	CAnimationRandomizer* m_animRandomizer;
	
	// kludge
	float m_animationLength;

	// remember to update IsLessThan, SerializeAsFile
	uint8 m_layer;
	uint8 m_flags;
};

template <int N>
class CAGAnimationLayer : public CAGAnimation
{
public:
	CAGAnimationLayer() : CAGAnimation(N) {}
};

#endif
